#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <omp.h>

#define NUMERO_CAMADAS 3

long int smooth(long int **camada, long int altura, long int largura, int pos_altura, int pos_largura){     
     int i,j;
     long int valor = 0;
     for(i=pos_altura-2; i<=pos_altura+2; i++){     
         for(j=pos_largura-2; j<=pos_largura+2; j++){
             if(i<0 || j<0 || i>=altura || j>=largura)                   
                    valor += 0;                          
             else valor += camada[i][j];
         }            
     }  
     valor = valor / 25;    
     return valor;
}

long int **aloca_matriz(long int rows, long int cols) { 
    int i;
    long int *data = (long int *)malloc(rows*cols*sizeof(long int));
    long int **array= (long int **)malloc(rows*sizeof(long int*));
    for (i=0; i<rows; i++)
        array[i] = &(data[cols*i]);

    return array;
}

int main (int argc, char *argv[]){

    int numero_processos, id_processo, emissor, destino, i, j;          

    MPI_Status status;

    MPI_Init(&argc,&argv);	
    MPI_Comm_rank(MPI_COMM_WORLD,&id_processo);	
	
    if (id_processo == 0){
		FILE *arquivo_inicial,*arquivo_final;                     
		unsigned char cabecalho_antes[18], cabecalho_depois[28];  

		long int largura = 0, altura = 0;            
	
		long int **vermelho, **verde, **azul, **smooth_vermelho, **smooth_verde, **smooth_azul;     

		struct timeval tempo_inicial;
    	struct timeval tempo_final;
    	gettimeofday( &tempo_inicial, NULL ); 

		arquivo_inicial = fopen(argv[1],"r+b");
		
			if(arquivo_inicial == NULL)
				printf("\n\nErro ao abrir o arquivo. Verificar se ele existe...\n\n");
       
			else{
				rewind(arquivo_inicial);
			
				// leitura do cabeçalho
				for(i=0;i<18;i++)
					fread(&cabecalho_antes[i],sizeof(unsigned char),1,arquivo_inicial);  
				fread(&largura,sizeof(unsigned char),4,arquivo_inicial);  
				fread(&altura,sizeof(unsigned char),4,arquivo_inicial);  
				for(i=0;i<28;i++)
					fread(&cabecalho_depois[i],1,1,arquivo_inicial);
		   
				vermelho = aloca_matriz(altura, largura);
				verde = aloca_matriz(altura, largura);
				azul = aloca_matriz(altura, largura);

				for(i=0;i<altura;i++){
					for(j=0;j<largura;j++){            
						vermelho[i][j] = 0;
						verde[i][j] = 0;
						azul[i][j] = 0;
				   
						fread(&vermelho[i][j],1,1,arquivo_inicial);  
						fread(&verde[i][j],1,1,arquivo_inicial);
						fread(&azul[i][j],1,1,arquivo_inicial);
					} 
				}
			
				for (destino=1; destino<=NUMERO_CAMADAS; destino++){            
					if(destino == 1){
						MPI_Send(&altura, 1, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
						MPI_Send(&largura, 1, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
						MPI_Send(&vermelho[0][0], altura*largura, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
					}
					else if(destino == 2){
						MPI_Send(&altura, 1, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
						MPI_Send(&largura, 1, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
						MPI_Send(&verde[0][0], altura*largura, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
					}
					else if(destino == 3){
						MPI_Send(&altura, 1, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
						MPI_Send(&largura, 1, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
						MPI_Send(&azul[0][0], altura*largura, MPI_UNSIGNED, destino, 1, MPI_COMM_WORLD);
					}	
				}

				smooth_vermelho = aloca_matriz(altura, largura);
				smooth_verde = aloca_matriz(altura, largura);
				smooth_azul = aloca_matriz(altura, largura);        

				for (i=1; i<=NUMERO_CAMADAS; i++){
					emissor = i;
				if(emissor == 1)
						MPI_Recv(&smooth_vermelho[0][0], altura*largura, MPI_UNSIGNED, emissor, 1, MPI_COMM_WORLD, &status);
				else if(emissor == 2)
						MPI_Recv(&smooth_verde[0][0], altura*largura, MPI_UNSIGNED, emissor, 1, MPI_COMM_WORLD, &status);
				else if(emissor == 3)
						MPI_Recv(&smooth_azul[0][0], altura*largura, MPI_UNSIGNED, emissor, 1, MPI_COMM_WORLD, &status);			   
				}

				arquivo_final = fopen(argv[2],"wb+");			
				for(i=0;i<18;i++)
					fwrite(&cabecalho_antes[i],sizeof(unsigned char),1,arquivo_final);  			   
				fwrite(&largura,sizeof(unsigned char),4,arquivo_final);  
				fwrite(&altura,sizeof(unsigned char),4,arquivo_final);  			
				for(i=0;i<28;i++)
					fwrite(&cabecalho_depois[i],1,1,arquivo_final);
			   
				for(i=0;i<altura;i++){
					for(j=0;j<largura;j++){				   
						fwrite(&smooth_vermelho[i][j],1,1,arquivo_final);  
						fwrite(&smooth_verde[i][j],1,1,arquivo_final);
						fwrite(&smooth_azul[i][j],1,1,arquivo_final);
				  
					} 
				}

				fclose(arquivo_final);
				 
				gettimeofday( &tempo_final, NULL ); 
    printf("\nTempo de processamento MPI: %d milisegundos.\n\n", (int) (1000 * (tempo_final.tv_sec - tempo_inicial.tv_sec) + (tempo_final.tv_usec - tempo_inicial.tv_usec) / 1000));
			}
		fclose(arquivo_inicial); 
	}
	else 
		if(id_processo == 1){
            long int largura=0, altura=0, **vermelho, **smooth_vermelho;         
                             
			MPI_Recv(&altura, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
			MPI_Recv(&largura, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
					
			vermelho = aloca_matriz(altura, largura);
			smooth_vermelho = aloca_matriz(altura, largura);               
			MPI_Recv(&vermelho[0][0], altura*largura, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
			
			for(i=0;i<altura;i++){
				for(j=0;j<largura;j++){
					smooth_vermelho[i][j] = smooth(vermelho,altura,largura,i,j); 
			   	} 
			}
			
			MPI_Send(&smooth_vermelho[0][0], altura*largura, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD);			
		}
		else 
			if(id_processo == 2){
				long int largura=0, altura=0, **verde, **smooth_verde;           
								 
				MPI_Recv(&altura, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
				MPI_Recv(&largura, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
			
				verde = aloca_matriz(altura, largura);
				smooth_verde = aloca_matriz(altura, largura);  		

				MPI_Recv(&verde[0][0], altura*largura, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
				
				for(i=0;i<altura;i++){
					for(j=0;j<largura;j++){		   
						smooth_verde[i][j] = smooth(verde,altura,largura,i,j); 		   
					} 
				}
				MPI_Send(&smooth_verde[0][0], altura*largura, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD);
			}
			else 
				if(id_processo == 3){
					long int largura=0, altura=0, **azul, **smooth_azul;     
								 
					MPI_Recv(&altura, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
					MPI_Recv(&largura, 1, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);

					azul = aloca_matriz(altura, largura);
					smooth_azul = aloca_matriz(altura, largura);		

					MPI_Recv(&azul[0][0], altura*largura, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD, &status);
					
					for(i=0;i<altura;i++){
						for(j=0;j<largura;j++){			   
							smooth_azul[i][j] = smooth(azul,altura,largura,i,j); 			   
						} 
					}
					MPI_Send(&smooth_azul[0][0], altura*largura, MPI_UNSIGNED, 0, 1, MPI_COMM_WORLD);
				}
    MPI_Finalize();
}


